package com.whz.generic;

import lombok.experimental.UtilityClass;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.joda.time.convert.ConverterManager;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * 时间工具类
 *
 * @author whz
 */
@UtilityClass
public class DateUtils {
    /**
     * 时间格式常量
     */
    public static final String COMMON_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final String COMMON_PATTERN_TYPE2 = "yyyy/MM/dd HH:mm:ss";
    public static final String SHORT_PATTERN = "yyyy-MM-dd";
    public static final String SHORT_PATTERN_TYPE2 = "yyyy/MM/dd";
    public static final String LONG_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";
    public static final String SUP_SHORT_PATTERN = "yyyyMMdd";
    public static final String SUP_LONG_PATTERN = "yyyyMMddHHmmss";
    public static final String YEAR_MONTH = "yyyyMM";
    public static final String CN_SHORT_PATTERN = "yyyy年MM月dd日";
    public static final String DDMM_PATTERN = "ddMM";

    /**
     * 将 日期String 解析为 java date
     *
     * @param dateString 待解析的日期字符串
     * @param pattern    日期字符串的时间格式
     * @return java date
     */
    public static Date toJavaDate(String dateString, String pattern) {
        String fm = defautIfEmpty(pattern);
        DateTimeFormatter format = DateTimeFormat.forPattern(fm);
        DateTime dt = DateTime.parse(dateString, format);
        return dt.toDate();
    }

    /**
     * 日期字符串格式化输出
     *
     * @param dateString  日期字符串
     * @param fromPattern 源日期格式
     * @param toPattern   目标日期个事
     * @return 目标格式的时间字符串
     */
    public static String toSpecialString(String dateString, String fromPattern, String toPattern) {
        String fm = defautIfEmpty(toPattern);
        DateTimeFormatter format = DateTimeFormat.forPattern(fromPattern);
        DateTime dt = DateTime.parse(dateString, format);
        return dt.toString(fm);
    }

    /**
     * Date类型日期转字符串格式化输出
     *
     * @param date    待格式化的日期
     * @param pattern 格式化后的格式
     * @param <T>     {@link ConverterManager}
     * @return 格式化后的时间
     */
    public static <T> String toSpecialString(T date, String pattern) {
        String fm = defautIfEmpty(pattern);
        DateTimeFormatter format = DateTimeFormat.forPattern(fm);
        return new DateTime(date).toString(format);
    }

    private static String defautIfEmpty(String pattern) {
        return StringUtils.isEmpty(pattern)? COMMON_PATTERN : pattern;
    }

    /**
     * 增加天数后的Date日期
     *
     * @param date      起点日期（date/string）
     * @param daysCount 增加天数
     * @param <T>       参见{@link ConverterManager}
     * @return 格式化后的时间
     */
    public static <T> Date addDays(T date, int daysCount) {
        DateTime dt = new DateTime(date);
        return dt.plusDays(daysCount).toDate();
    }

    /**
     * 减少天数后的日期
     *
     * @param date      起点日期（date/string）
     * @param daysCount 减少的天数
     * @return java date
     */
    public static <T> Date subDays(T date, int daysCount) {
        DateTime dt = new DateTime(date);
        return dt.minusDays(daysCount).toDate();
    }

    /**
     * 增加月数后的日期
     *
     * @param date   起点日期（date/string）
     * @param months 增加月数
     * @return java date
     */
    public static <T> Date addMonths(T date, int months) {
        DateTime dt = new DateTime(date);
        return dt.plusMonths(months).toDate();
    }

    /**
     * 减少月数后的日期
     *
     * @param date   时间
     * @param months 减去的月数
     * @param <T>    the datetime object, null means now
     * @return java date
     */
    public static <T> Date subMonths(T date, int months) {
        DateTime dt = new DateTime(date);
        return dt.minusMonths(months).toDate();
    }

    /**
     * 减少年数后的日期
     *
     * @param date  时间
     * @param years 减去的年数
     * @param <T>   the datetime object, null means now
     * @return java date
     */
    public static <T> Date subYears(T date, int years) {
        DateTime dt = new DateTime(date);
        return dt.minusYears(years).toDate();
    }

    /**
     * 计算月份差
     *
     * @param startTime 起始时间
     * @param endTime   终止时间
     * @return 两个时间间隔的月数
     */
    public static int betweenMonths(String startTime, String endTime) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        Calendar bef = Calendar.getInstance();
        Calendar aft = Calendar.getInstance();
        bef.setTime(sdf.parse(startTime));
        aft.setTime(sdf.parse(endTime));
        int result = aft.get(Calendar.MONTH) - bef.get(Calendar.MONTH);
        int month = (aft.get(Calendar.YEAR) - bef.get(Calendar.YEAR)) * 12;
        return month + result;
    }

    /**
     * 计算两个日期的间隔天数
     *
     * @param dateMin 起点日期（date/string）
     * @param dateMax 结束日期（date/string）
     * @return 两个时间间隔的天数
     */
    public static <T> int betweenDays(T dateMin, T dateMax) {
        DateTime start = new DateTime(dateMin);
        DateTime end = new DateTime(dateMax);
        return Days.daysBetween(start, end).getDays();
    }

    /**
     * 计算两个时间的毫秒间隔
     *
     * @param dateMin 起点日期（date/string）
     * @param dateMax 结束日期（date/string）
     * @return 两个时间间隔的毫秒数
     */
    public static <T> long betweenMillis(T dateMin, T dateMax) {
        DateTime start = new DateTime(dateMin);
        DateTime end = new DateTime(dateMax);
        return end.getMillis() - start.getMillis();
    }

    /**
     * 判断是否是周末
     *
     * @param dt 日期
     * @return true 周末;false 不是周末
     */
    public static boolean isWeekEnd(DateTime dt) {
        int wd = dt.dayOfWeek().get();
        return wd == 6 || wd == 7;
    }

    /**
     * 18、判断目标时间是否在区间内
     *
     * @param startDate  开始时间date/string
     * @param endDate    结束时间date/string
     * @param targetDate 目标时间date/string
     * @param <T>        {@link ConverterManager}
     * @return true 在时间区间内
     */
    public static <T> boolean isInRange(T startDate, T endDate, T targetDate) {
        DateTime dtLow = new DateTime(startDate);
        DateTime dtHigh = new DateTime(endDate);
        DateTime dt = new DateTime(targetDate);
        return dt.getMillis() >= dtLow.getMillis() && dt.getMillis() <= dtHigh.getMillis();
    }

    /**
     * 现在是否在时间区间之内
     *
     * @param startDate 开始时间date/string
     * @param endDate   结束时间date/string
     * @return true:现在处于时间区间内
     */
    public static <T> boolean isInRange(T startDate, T endDate) {
        return isInRange(startDate, endDate, DateTime.now());
    }

    /**
     * 按指定格式获取当前系统日期时间
     *
     * @param pattern 时间格式
     * @return time string
     */
    public static String getCurrentDateTimeStr(String pattern) {
        return toSpecialString(Calendar.getInstance(), pattern);
    }

    /**
     * 获取Date日期类型start到end天数间隔
     *
     * @param start 起始时间
     * @param end   重点事件
     * @return 两个时间的间隔天数
     */
    public static long getDaysBetween(Date start, Date end) {
        return betweenDays(toLocalDate(start), toLocalDate(end));
    }

    public static LocalDate toLocalDate(Date d) {
        return new DateTime(d).toLocalDate();
    }

    public static long roundUpToNextMinute(long timeInMs) {
        Calendar date = Calendar.getInstance();
        date.setTimeInMillis(timeInMs);
        date.add(Calendar.MINUTE, 1);
        date.clear(Calendar.SECOND);
        date.clear(Calendar.MILLISECOND);
        return date.getTimeInMillis();
    }

    public static long roundDownMinute(long timeInMs) {
        Calendar date = Calendar.getInstance();
        date.setTimeInMillis(timeInMs);
        date.clear(Calendar.SECOND);
        date.clear(Calendar.MILLISECOND);
        return date.getTimeInMillis();
    }
}